#include <iostream>
#include "Firm.h"
#define ONSCREEN 0
using namespace std;

//// Methods for the superclass Firm
// Constructor
Firm::Firm () {}

void // Initializer
Firm::initialize (double input[]) {
    netWorth = input[16];
    wage = input[12];

    // Functions parameters
    phi = input[4];
    beta = input[5];
    deltaD = input[6];
    gamma = input[8];
    deltaU = input[7];
    alpha = input[9];
    sigma = input[10];
    theta = input[11];
}

void // Update of firm indicators and quantities to be bought and sold
Firm::calcQuantities () {}

void // Calculate interest rates
Firm::calcInterestRates () {}

void // Calculate profit and update net worth
Firm::calcNetWorth () {}

void // Pass debts to partners when going bankrupt
Firm::goBankrupt () {}

// Accessor methods
double
Firm::getNetWorth () {
    return netWorth;
}

double
Firm::getProfit () {
    return profit;
}

double
Firm::getTradeIR () {
    return tradeIR;
}

double
Firm::getBankIR () {
    return bankIR;
}

double
Firm::getLoan () {
    return bankLoan;
}

double
Firm::getLeverage () {
    return leverage;
}

bool
Firm::checkInsolvency () {
    bool isInsolvent = false;
    if (netWorth <= 0) isInsolvent = true;
    return isInsolvent;
}

//// Methods for the DownstreamFirm class, which inherits variables and methods
//// from the superclass Firm
// Constructor
DownstreamFirm::DownstreamFirm () : Firm() {}

void // Initializer
DownstreamFirm::initialize (double input[]) {
    Firm::initialize (input);
}

void // Update of firm indicators and quantities to be bought and sold
DownstreamFirm::calcQuantities (double sp) {
    sellQuant = phi * pow (netWorth, beta);
    sellPrice = sp;
    numWorkers = deltaD * sellQuant;
    buyQuant = gamma * sellQuant;

    if (wage * numWorkers > netWorth) bankLoan = wage * numWorkers - netWorth;
    else bankLoan = 0;
    leverage = bankLoan/netWorth;

    if (ONSCREEN == 1) cout << "sellQuant: " << sellQuant << " numWorkers: " << numWorkers << " buyQuant: " << buyQuant << " sellPrice: " << sellPrice << " bankLoan: " << bankLoan << "\n";
}

void // Calculate interest rates
DownstreamFirm::calcInterestRates () {
    tradeIR = partnerFirm->getTradeIR ();
    bankIR = partnerBank->getBankIR (leverage);
    if (ONSCREEN == 1) cout << "tradeIR: " << tradeIR << " bankIR: " << bankIR << "\n";
}

void // Calculate profit and update net worth
DownstreamFirm::calcNetWorth () {
    buyPrice = 1 + tradeIR;
    profit = sellPrice * sellQuant - bankIR * bankLoan - buyPrice * buyQuant;
    netWorth += profit;
    if (ONSCREEN == 1) cout << "profit: " << profit << " netWorth: " << netWorth << "\n";
}

void // Pass debts to partners when going bankrupt
DownstreamFirm::goBankrupt () {
    partnerBank->delDstrPartner (this);
    partnerBank->incrBadDebt (bankLoan);
    partnerBank = NULL;

    partnerFirm->delDstrPartner (this);
    partnerFirm->incrBadDebt (buyPrice * buyQuant);
    partnerFirm = NULL;
}

// Methods to deal with the pointers
void
DownstreamFirm::setUstrPartner (UpstreamFirm *p) {
    if (partnerFirm != NULL) partnerFirm->delDstrPartner (this);
    partnerFirm = p;
}

void
DownstreamFirm::delUstrPartner () {
    partnerFirm = NULL;
}

void
DownstreamFirm::setBankPartner (Bank *b) {
    if (partnerBank != NULL) partnerBank->delDstrPartner (this);
    partnerBank = b;
}

void
DownstreamFirm::delBankPartner () {
    partnerBank = NULL;
}

// Accessor methods
double
DownstreamFirm::getBuyQuant () {
    return buyQuant;
}

double
DownstreamFirm::getProduction () {
    return sellQuant;
}

bool
DownstreamFirm::gotNoUstrPartner () {
    bool gotNo = false;
    if (partnerFirm == NULL) gotNo = true;
    return gotNo;
}

bool
DownstreamFirm::gotNoBankPartner () {
    bool gotNo = false;
    if (partnerBank == NULL) gotNo = true;
    return gotNo;
}

//// Methods for the UpstreamFirm class, which inherits variables and methods
//// from the superclass Firm
// Constructor
UpstreamFirm::UpstreamFirm () : Firm() {}

void // Initializer
UpstreamFirm::initialize (double input[]) {
    Firm::initialize (input);

    numDstrPartners = 0;
    partnerFirms = new DownstreamFirm *[static_cast <int> (input[0])];
}

void // Update of firm indicators and quantities to be bought and sold
UpstreamFirm::calcQuantities () {
    sellQuant = 0;
    for (int i = 0; i < numDstrPartners; i++)
        sellQuant += partnerFirms[i]->getBuyQuant ();
    numWorkers = deltaU * sellQuant;
    badDebt = 0;

    if (wage * numWorkers > netWorth) bankLoan = wage * numWorkers - netWorth;
    else bankLoan = 0;
    leverage = bankLoan/netWorth;

    if (ONSCREEN == 1) cout << "sellQuant: " << sellQuant << " numWorkers: " << numWorkers << " bankLoan: " << bankLoan << "\n";
}

void // Calculate interest rates
UpstreamFirm::calcInterestRates () {
    tradeIR = alpha * pow (netWorth, -alpha);
    bankIR = partnerBank->getBankIR (leverage);
    if (ONSCREEN == 1) cout << "tradeIR: " << tradeIR << " bankIR: " << bankIR << "\n";
}

void // Calculate profit and update net worth
UpstreamFirm::calcNetWorth () {
    sellPrice = 1 + tradeIR;
    profit = sellPrice * sellQuant - bankIR * bankLoan;
    netWorth += (profit - badDebt);
    if (ONSCREEN == 1) cout << "profit: " << profit << " badDebt: " << badDebt << " netWorth: " << netWorth << "\n";
}

void // Pass debts to partners when going bankrupt
UpstreamFirm::goBankrupt () {
    partnerBank->delUstrPartner (this);
    partnerBank->incrBadDebt (bankLoan);
    partnerBank = NULL;

    for (int i = 0; i < numDstrPartners; i++) partnerFirms[i]->delUstrPartner ();
    if (partnerFirms != NULL) delete[] partnerFirms;
}

// Methods to deal with the pointers
void
UpstreamFirm::addDstrPartner (DownstreamFirm *p) {
    partnerFirms[numDstrPartners] = p;
    numDstrPartners ++;
}

void
UpstreamFirm::delDstrPartner (DownstreamFirm *p) {
    int pos = -1;

    for (int i = 0; i < numDstrPartners; i++) if (partnerFirms[i] == p) pos = i;

    if (pos != -1) {
        numDstrPartners --;
        for (int i = pos; i < numDstrPartners; i++) partnerFirms[i] = partnerFirms[i+1];
    }
}

void
UpstreamFirm::setBankPartner (Bank *b) {
    if (partnerBank != NULL) partnerBank->delUstrPartner (this);
    partnerBank = b;
}

void
UpstreamFirm::delBankPartner () {
    partnerBank = NULL;
}

// Accessor methods
void
UpstreamFirm::incrBadDebt (double bd) {
    badDebt += bd;
}

bool
UpstreamFirm::gotNoBankPartner () {
    bool gotNo = false;
    if (partnerBank == NULL) gotNo = true;
    return gotNo;
}

int
UpstreamFirm::getNumPartners () {
    return numDstrPartners;
}
